package Renderer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import javax.media.opengl.GL2;
import LDraw.Support.MatrixMath;
import com.jogamp.common.nio.Buffers;
public class LDrawDLBuilder {
/**
* @uml.property name="flags"
* @uml.associationEnd
* qualifier="dl_has_meta:Renderer.LDrawDLT java.lang.Boolean"
*/
HashMap<LDrawDLT, Boolean> flags;
/**
* @uml.property name="head"
* @uml.associationEnd
*/
LDrawDLBuilderPerTex head = null;;
/**
* @uml.property name="cur"
* @uml.associationEnd
*/
LDrawDLBuilderPerTex cur = null;
// ========== LDrawDLBuilderCreate
// ================================================
//
// Purpose: Create a new builder capable of accumulating DL data.
//
// ================================================================================
public LDrawDLBuilder() {
flags = new HashMap<LDrawDLT, Boolean>();
// All allocs for the builder come from one pool.
// Build one tex now for the untextured set of meshes, which are
// the default state.
LDrawDLBuilderPerTex untex = new LDrawDLBuilderPerTex();
setCur(untex);
setHead(untex);
}
/**
* @param head
* @uml.property name="head"
*/
private void setHead(LDrawDLBuilderPerTex head) {
this.head = head;
}
/**
* @param cur
* @uml.property name="cur"
*/
private void setCur(LDrawDLBuilderPerTex cur) {
this.cur = cur;
}
// ========== LDrawDLBuilderAddQuad
// ===============================================
//
// Purpose: Add one quad to the current DL builder in the current texture.
//
// ================================================================================
public void addQuad(float[] v, float[] n, float[] c) {
if (MatrixMath.compareFloat(c[3], 0.0f) == 0)
flags.put(LDrawDLT.dl_has_meta, true);
else if (MatrixMath.compareFloat(c[3], 1.0f) != 0)
flags.put(LDrawDLT.dl_has_alpha, true);
int i;
LDrawDLBuilderVertexLink nl = new LDrawDLBuilderVertexLink(4,
LDrawDisplayList.VERT_STRIDE);
nl.setNext(null);
nl.setVcount(4);
float[] data = nl.getData();
for (i = 0; i < 4; ++i) {
MatrixMath.copy_vec3(data, LDrawDisplayList.VERT_STRIDE * i, v,
i * 3);
MatrixMath.copy_vec3(data, LDrawDisplayList.VERT_STRIDE * i + 3, n,
0);
MatrixMath.copy_vec4(data, LDrawDisplayList.VERT_STRIDE * i + 6, c,
0);
}
if (cur.getQuad_tail() != null) {
cur.getQuad_tail().setNext(nl);
cur.setQuad_tail(nl);
} else {
cur.setQuad_head(nl);
cur.setQuad_tail(nl);
}
}
// ========== LDrawDLBuilderFinish
// ================================================
//
// Purpose: Take all of the accumulated data in a DL and bake it down to one
// final form.
//
// Notes: The DL is, while being built, a series of linked lists in a BDP
// for
// speed. The finished DL is a malloc'd block of memory, pre-sized to
// fit the DL perfectly, and one VBO. So this routine does the counting,
// final allocations, and copying.
//
// ================================================================================
public LDrawDL finish(GL2 gl2) {
int total_texes = 0;
int total_tris = 0;
int total_quads = 0;
int total_lines = 0;
LDrawDLBuilderVertexLink l;
LDrawDLBuilderPerTex s;
// Count up the total vertices we will need, for VBO space, as well
// as the total distinct non-empty textures.
for (s = head; s != null; s = s.getNext()) {
if (s.getTri_head() != null || s.getLine_head() != null
|| s.getQuad_head() != null)
++total_texes;
for (l = s.getTri_head(); l != null; l = l.getNext()) {
total_tris += l.getVcount();
}
for (l = s.getQuad_head(); l != null; l = l.getNext()) {
total_quads += l.getVcount();
}
for (l = s.getLine_head(); l != null; l = l.getNext()) {
total_lines += l.getVcount();
}
}
// No non-empty textures? Bail out early - nuke our
// context and get out. Client code knows we get NO DL, rather than
// an empty one.
if (total_texes == 0) {
return null;
}
// Malloc DL structure with extra storage for variable-sized tex array.
LDrawDL dl = new LDrawDL(total_texes);
// All per-session linked list ptrs start null.
dl.setNext(null);
dl.setInstance_head(null);
dl.setInstance_tail(null);
dl.setInstance_count(0);
dl.setTex_count(total_texes);
LDrawDLPerTex cur_tex = dl.getTexes()[0];
dl.setFlags(flags);
total_tris /= 3;
total_quads /= 4;
total_lines /= 2;
// We use one mesh for the entire DL, even if it has multiple textures.
// We have to
// do this because we wnat smoothing across triangles that do not share
// the same
// texture. (Key use case: minifig faces are part textured, part
// untextured.)
//
// So instead each face gets a texture ID (tid), which is an index that
// we will tie
// to our texture list. The mesh smoother remembers this and dumps out
// the tris in
// tid order later.
Mesh M = new Mesh(total_tris, total_quads, total_lines);
// Now: walk our building textures - for each non-empty one, we will
// copy it into
// the tex array and push its vertices.
int ti = 0;
for (s = head; s != null; s = s.getNext()) {
if (s.getTri_head() == null && s.getLine_head() == null
&& s.getQuad_head() == null)
continue;
if (s.getSpec().getTex_obj() != 0)
dl.flags.put(LDrawDLT.dl_has_tex, true);
float[] p1 = new float[3];
float[] p2 = new float[3];
float[] p3 = new float[3];
float[] p4 = new float[3];
float[] color = new float[4];
for (l = s.getTri_head(); l != null; l = l.getNext()) {
System.arraycopy(l.data, 0, p1, 0, 3);
System.arraycopy(l.data, 10, p2, 0, 3);
System.arraycopy(l.data, 20, p3, 0, 3);
System.arraycopy(l.data, 6, color, 0, 4);
M.add_face(p1, p2, p3, null, color, ti);
}
for (l = s.getQuad_head(); l != null; l = l.getNext()) {
System.arraycopy(l.data, 0, p1, 0, 3);
System.arraycopy(l.data, 10, p2, 0, 3);
System.arraycopy(l.data, 20, p3, 0, 3);
System.arraycopy(l.data, 30, p4, 0, 3);
System.arraycopy(l.data, 6, color, 0, 4);
M.add_face(p1, p2, p3, p4, color, ti);
}
++ti;
}
ti = 0;
for (s = head; s != null; s = s.getNext()) {
if (s.getTri_head() == null && s.getLine_head() == null
&& s.getQuad_head() == null)
continue;
if (s.getSpec().getTex_obj() != 0)
dl.getFlags().put(LDrawDLT.dl_has_tex, true);
float[] p1 = new float[3];
float[] p2 = new float[3];
float[] color = new float[4];
for (l = s.getLine_head(); l != null; l = l.getNext()) {
System.arraycopy(l.data, 0, p1, 0, 3);
System.arraycopy(l.data, 10, p2, 0, 3);
System.arraycopy(l.data, 6, color, 0, 4);
M.add_face(p1, p2, null, null, color, ti);
}
++ti;
}
M.finish_faces_and_sort();
M.add_creases();
M.find_and_remove_t_junctions();
M.finish_creases_and_join();
M.smooth_vertices();
M.merge_vertices();
IntBuffer total_vertices, total_indices;
total_vertices = IntBuffer.allocate(1);
total_indices = IntBuffer.allocate(1);
M.get_final_mesh_counts(total_vertices, total_indices);
ByteBuffer byteBufferForVertex = Buffers
.newDirectByteBuffer(total_vertices.get(0)
* LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8);
FloatBuffer vertex_ptr = byteBufferForVertex.asFloatBuffer();
ByteBuffer byteBufferForIndex = Buffers
.newDirectByteBuffer(total_indices.get(0) * Integer.SIZE / 8);
IntBuffer index_ptr = byteBufferForIndex.asIntBuffer();
// Grab variable size arrays for the start/offsets of each sub-part of
// our big pile-o-mesh...
// the mesher will give us back our tris sorted by texture.
IntBuffer line_start = IntBuffer.allocate(total_texes);
IntBuffer line_count = IntBuffer.allocate(total_texes);
IntBuffer tri_start = IntBuffer.allocate(total_texes);
IntBuffer tri_count = IntBuffer.allocate(total_texes);
IntBuffer quad_start = IntBuffer.allocate(total_texes);
IntBuffer quad_count = IntBuffer.allocate(total_texes);
M.write_indexed_mesh(total_vertices.get(0), vertex_ptr,
total_indices.get(0), index_ptr, 0, line_start, line_count,
tri_start, tri_count, quad_start, quad_count);
gl2.glGenBuffers(1, dl.geo_vbo);
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER, dl.geo_vbo.get(0));
gl2.glBufferData(GL2.GL_ARRAY_BUFFER, byteBufferForVertex.capacity(),
byteBufferForVertex, GL2.GL_STATIC_DRAW);
gl2.glGenBuffers(1, dl.idx_vbo);
gl2.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, dl.idx_vbo.get(0));
gl2.glBufferData(GL2.GL_ELEMENT_ARRAY_BUFFER,
byteBufferForIndex.capacity(), byteBufferForIndex,
GL2.GL_STATIC_DRAW);
ti = 0;
for (s = head; s != null; s = s.next) {
try {
cur_tex.spec = (LDrawTextureSpec) s.spec.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cur_tex.quad_off = quad_start.get(ti);
cur_tex.line_off = line_start.get(ti);
cur_tex.tri_off = tri_start.get(ti);
cur_tex.quad_count = quad_count.get(ti);
cur_tex.line_count = line_count.get(ti);
cur_tex.tri_count = tri_count.get(ti);
++ti;
cur_tex = cur_tex.next();
}
M.destroy_mesh();
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
gl2.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
return dl;
}
// ========== LDrawDLBuilderAddLine
// ===============================================
//
// Purpose: Add one line to the current DL builder in the current texture.
//
// ================================================================================
public void addLine(float[] v, float[] n, float[] c) {
if (MatrixMath.compareFloat(c[3], 0.0f) == 0)
flags.put(LDrawDLT.dl_has_meta, true);
else if (MatrixMath.compareFloat(c[3], 1.0f) != 0)
flags.put(LDrawDLT.dl_has_alpha, true);
int i;
LDrawDLBuilderVertexLink nl = new LDrawDLBuilderVertexLink(2,
LDrawDisplayList.VERT_STRIDE);
nl.setNext(null);
nl.setVcount(2);
for (i = 0; i < 2; ++i) {
MatrixMath.copy_vec3(nl.data, LDrawDisplayList.VERT_STRIDE * i, v,
i * 3);
MatrixMath.copy_vec3(nl.data, LDrawDisplayList.VERT_STRIDE * i + 3,
n, 0);
MatrixMath.copy_vec4(nl.data, LDrawDisplayList.VERT_STRIDE * i + 6,
c, 0);
}
if (cur.getLine_tail() != null) {
cur.getLine_tail().setNext(nl);
cur.setLine_tail(nl);
} else {
cur.setLine_head(nl);
cur.setLine_tail(nl);
}
}
// ========== LDrawDLBuilderAddTri
// ================================================
//
// Purpose: Add one triangle to our DL using the current texture.
//
// Notes: This routine 'sniffs' the alpha as it goes by and keeps the DL
// flags
// correct - this is how a DL "knows" if it is translucent.
//
// We accumulate the tri by allocating a 3-vertex DL link and queueing it
// onto the triangle list for the current texture.
//
// ================================================================================
public void addTri(float[] v, float[] n, float[] c) {
// Alpha = 0 means meta color. 0 < Alpha < 1 means translucency.
if (MatrixMath.compareFloat(c[3], 0.0f) == 0)
flags.put(LDrawDLT.dl_has_meta, true);
else if (MatrixMath.compareFloat(c[3], 1.0f) != 0)
flags.put(LDrawDLT.dl_has_alpha, true);
int i;
LDrawDLBuilderVertexLink nl = new LDrawDLBuilderVertexLink(3,
LDrawDisplayList.VERT_STRIDE);
nl.setNext(null);
nl.setVcount(3);
float data[] = nl.getData();
for (i = 0; i < 3; ++i) {
MatrixMath.copy_vec3(data, LDrawDisplayList.VERT_STRIDE * i, v,
i * 3); // Vertex data is per
// vertex.
MatrixMath.copy_vec3(data, LDrawDisplayList.VERT_STRIDE * i + 3, n,
0);// But color and norm
// are for the whole
// tri, for now. So
// we replicate it
// out to get
MatrixMath.copy_vec4(data, LDrawDisplayList.VERT_STRIDE * i + 6, c,
0);// a uniform DL.
}
if (cur.getTri_tail() != null) {
cur.getTri_tail().setNext(nl);
cur.setTri_tail(nl);
} else {
cur.setTri_head(nl);
cur.setTri_tail(nl);
}
}
}